package com.atguigu.exam.service.impl;

import com.atguigu.exam.entity.*;
import com.atguigu.exam.mapper.CategoryMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.CategoryService;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.utils.RedisUtils;
import com.atguigu.exam.vo.ChatChoice;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

import static com.atguigu.exam.common.CacheConstants.POPULAR_QUESTIONS_KEY;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Override
/*    public void getQuestionList(Page<Question> pageBean, QuestionQueryVo questionQueryVo) {

        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(questionQueryVo.getKeyword() !=null,Question::getTitle, questionQueryVo.getKeyword())
                .eq(questionQueryVo.getCategoryId() !=null,Question::getCategoryId, questionQueryVo.getCategoryId())
                .eq(questionQueryVo.getDifficulty()!=null,Question::getDifficulty, questionQueryVo.getDifficulty())
                .eq(questionQueryVo.getType()!=null,Question::getType, questionQueryVo.getType());
        queryWrapper.orderByDesc(Question::getCreateTime);

        Page<Question> questionPage = questionMapper.selectPage(pageBean, queryWrapper);

        List<Question> records = questionPage.getRecords();

        for (Question record : records) {
            if ("CHOICE".equals(record.getType())){
                LambdaQueryWrapper<QuestionChoice> questionChoiceQueryWrapper = new LambdaQueryWrapper<>();
                questionChoiceQueryWrapper.eq(QuestionChoice::getQuestionId, record.getId());
                record.setChoices(questionChoiceMapper.selectList(questionChoiceQueryWrapper));
            }

            LambdaQueryWrapper<QuestionAnswer> questionAnswerQueryWrapper = new LambdaQueryWrapper<>();
            questionAnswerQueryWrapper.eq(QuestionAnswer::getQuestionId, record.getId());
            record.setAnswer(questionAnswerMapper.selectOne(questionAnswerQueryWrapper));

            record.setCategory(categoryMapper.selectById(record.getCategoryId()));
        }

        questionPage.setRecords(records);
    }*/


    public void getQuestionList(Page<Question> pageBean, QuestionQueryVo questionPageVo) {
        //1.分页查询题目列表（多条件）
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getType()), Question::getType, questionPageVo.getType());
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getDifficulty()), Question::getDifficulty, questionPageVo.getDifficulty());
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getCategoryId()), Question::getCategoryId, questionPageVo.getCategoryId());
        lambdaQueryWrapper.like(!ObjectUtils.isEmpty(questionPageVo.getKeyword()), Question::getTitle, questionPageVo.getKeyword());
        //时间的倒序排序！！
        lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
        page(pageBean, lambdaQueryWrapper);
        //2.提取一个方法， 给题目进行选项和答案装填（热门题目也需要所以提取方法）
        fillQuestionChoiceAndAnswer(pageBean.getRecords());
    }

    /**
     * 根据ID查询题目详情
     *
     * @param id
     */
    @Override
    public Question getQuestionById(Long id) {
        Question question = questionMapper.selectById(id);

        if (question != null) {
            if ("CHOICE".equals(question.getType())) {
                //查询选项
                List<QuestionChoice> questionChoiceList = questionChoiceMapper.selectList(new LambdaQueryWrapper<QuestionChoice>()
                        .eq(QuestionChoice::getQuestionId, id));
                question.setChoices(questionChoiceList);
            }
            //获取题的答案
            QuestionAnswer questionAnswer = questionAnswerMapper.selectOne(new LambdaQueryWrapper<QuestionAnswer>()
                    .eq(QuestionAnswer::getQuestionId, id));
            question.setAnswer(questionAnswer);

            question.setCategory(categoryMapper.selectById(question.getCategoryId()));
        }

        //热点题目缓存
        new Thread(() -> {
            if (question != null) {
                incrementQuestion(question.getId());
            }
        }).start();
        return question;

    }

    /**
     * 创建题目
     *
     * @param question
     */
    @Override
    @Transactional
    public void createQuestion(Question question) {
        // 参数校验
        if (question == null) {
            throw new IllegalArgumentException("题目信息不能为空");
        }

        // 检查同一类型下是否已存在相同标题的题目
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getTitle, question.getTitle())
                .eq(Question::getType, question.getType());
        if (questionMapper.selectCount(queryWrapper) > 0) {
            throw new RuntimeException("同类型下已存在相同标题的题目: " + question.getTitle());
        }

        questionMapper.insert(question);
        Long questionId = question.getId();

        // 插入答案
        QuestionAnswer answer = question.getAnswer();
        if (answer != null) {
            answer.setQuestionId(questionId);
            questionAnswerMapper.insert(answer);
        } else {
            log.warn("题目(ID: {})没有设置答案", questionId);
        }

        // 如果是选择题，插入选项
        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choices = question.getChoices();
            if (choices != null && !choices.isEmpty()) {
                for (int i = 0; i < choices.size(); i++) {
                    QuestionChoice choice = choices.get(i);
                    choice.setQuestionId(questionId);
                    choice.setSort(i);
                    questionChoiceMapper.insert(choice);
                }
            } else {
                log.warn("选择题(ID: {})没有设置选项", questionId);
            }
        }
    }

    /**
     * 自定义更新题目
     *
     * @param question
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void customUpdateQuestion(Question question) {
        // 检查同一类型下是否已存在相同标题的题目（排除当前题目）
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getTitle, question.getTitle())
                .eq(Question::getType, question.getType())
                .ne(Question::getId, question.getId()); // 排除当前正在更新的题目
        if (questionMapper.selectCount(queryWrapper) > 0) {
            throw new RuntimeException("同类型下已存在相同标题的题目: " + question.getTitle());
        }

        // 更新题目
        boolean updated = updateById(question);
        if (!updated) {
            throw new RuntimeException("修改：%s题目失败！！".formatted(question.getId()));
        }
        
        Long questionId = question.getId();

        // 更新答案
        QuestionAnswer answer = question.getAnswer();
        
        // 如果是选择题，更新选项
        if ("CHOICE".equals(question.getType())) { 
            List<QuestionChoice> choices = question.getChoices();
            
            // 删除题目对应的所有选项（原） [根据题目id删除]
            LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(QuestionChoice::getQuestionId, questionId);
            questionChoiceMapper.delete(lambdaQueryWrapper);
            
            // 循环新增选项
            if (choices != null && !choices.isEmpty()) { 
                // 拼接正确的档案 a,b
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < choices.size(); i++) {
                    QuestionChoice choice = choices.get(i);
                    choice.setId(null);
                    // 确保正确顺序
                    choice.setSort(i);
                    choice.setCreateTime(null);
                    choice.setUpdateTime(null);
                    // 新增选项需要设置题目ID
                    choice.setQuestionId(questionId);
                    questionChoiceMapper.insert(choice);
                    if (choice.getIsCorrect() != null && choice.getIsCorrect()){
                        if (sb.length() > 0){
                            sb.append(",");
                        }
                        sb.append((char)('A'+i));
                    }
                }
                // 答案对象赋值选择题答案
                if (answer != null) {
                    answer.setAnswer(sb.toString());
                }
            } else {
                log.warn("选择题(ID: {})没有设置选项", questionId);
            }
        }
        
        if (answer != null) {
            answer.setQuestionId(questionId);
            questionAnswerMapper.updateById(answer);
        } else {
            log.warn("题目(ID: {})没有设置答案", questionId);
        }
    }

    /**
     * 删除题目
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteQuestion(Long id) {
        // 先查询题目是否存在
        Question question = getById(id);
        if (question == null) {
            return false; // 题目不存在，返回删除失败
        }

        //如果是选择题，删除选项
        if ("CHOICE".equals(question.getType())) {
            questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, id));
        }

        //删除答案
        questionAnswerMapper.delete(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId, id));

        //删除题目
        return removeById(id);
    }

    /**
     * 查询热门题目
     *
     * @param size
     * @return
     */
    @Override
    public List<Question> customFindPopularQuestions(Integer size) {
        // 边界条件检查
        if (size == null || size <= 0) {
            return new ArrayList<>();
        }
        
        //1. 定义热门题目集合（总集合）
        List<Question> popularQuestions = new ArrayList<>();

        //2. 去zset中获取热门题目，并且添加到总集合中
        // 获取题目排行，需要获取id和分数！ 分数用于后续的排序处理！
        Set<ZSetOperations.TypedTuple<Object>> tupleSet = redisUtils.zReverseRangeWithScores(POPULAR_QUESTIONS_KEY, 0, size - 1);
        //定义接收id的集合
        List<Long> idsSet = new ArrayList<>();
        if (tupleSet != null && !tupleSet.isEmpty()) {
            //根据排行榜的积分，倒序进行Id查询！
            List<Long> idsList = tupleSet.stream().sorted((o1, o2) -> Integer.compare(o2.getScore().intValue(), o1.getScore().intValue()))
                .map(o -> Long.valueOf(o.getValue().toString())).collect(Collectors.toList());
            //复制，用于后面补充！！
            idsSet.addAll(idsList);
            log.debug("从redis获取热门题目的id集合，且保证顺序：{}",idsList);

            for (Long id : idsList) {
                Question question = getById(id);
                if (question != null){
                    //防止redis有缓存，但是数据库中没有！ 后续优化，删除题目，应该删除热题榜单中对应的value
                    popularQuestions.add(question);
                }
            }
            log.debug("去redis查询的热门题目，题目数：{},题目内容为：{}",popularQuestions.size(),popularQuestions);
        }

        //3. 检查是否已经满足size
        int diff = size - popularQuestions.size();
        if(diff > 0){
            //4. 不满足，题目表中 非热门题目 时间倒序 limit 差数量
            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.notIn(Question::getId,idsSet);
            lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            //limit diff;
            lambdaQueryWrapper.last("limit " + diff);
            List<Question> questionDiffList = list(lambdaQueryWrapper);
            log.debug("去question表中补充热门题目，题目数：{},题目内容为：{}",questionDiffList.size(),questionDiffList);
            if (!questionDiffList.isEmpty()) {
                // 5. 补充也添加到总集合中
                popularQuestions.addAll(questionDiffList);
            }
        }
        //6. 总集合一起进行答案和选项填充
        fillQuestionChoiceAndAnswer(popularQuestions);
        //7. 返回即可
        return popularQuestions;
    }

    /**
     * 预览导入的Excel文件
     *
     * @param file
     * @return
     */
    @Override
    public List<QuestionImportVo> preViewExcel(MultipartFile file) throws IOException {
        //数据校验
        if (file == null || file.isEmpty()){
            throw new RuntimeException("预览数据的文件为空！");
        }
        String fileName = file.getOriginalFilename();
        //xls xlsx
        if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")){
            throw new RuntimeException("预览数据的文件格式错误，必须是 .xls或者.xlsx！");
        }
        //解析数据
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //返回结果
        return questionImportVoList;
    }
    /**
     * 批量题目导入 [execl和ai生成批量导入]
     * 批量数据库添加
     * 批量redis添加
     * @return Excel导入完成！成功导入 %d [工程导入] / %d [题目总数] 道题目
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int importQuestions(List<QuestionImportVo> questions) {
        //1. 进行数据校验
        if (questions == null || questions.isEmpty()){
            throw new RuntimeException("导入的题目数据为空！");
        }

        return extracted(questions);
    }

    private int extracted(List<QuestionImportVo> questions) {
        int count=0;
        for (QuestionImportVo questionImportVo : questions) {
            Question question = new Question();
            BeanUtils.copyProperties(questionImportVo, question);
            saveOrUpdate(question);

            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Question::getTitle, question.getTitle());
            lambdaQueryWrapper.eq(Question::getType, question.getType());
            lambdaQueryWrapper.eq(Question::getCategoryId, question.getCategoryId());
            lambdaQueryWrapper.eq(Question::getDifficulty, question.getDifficulty());
            lambdaQueryWrapper.eq(Question::getScore, question.getScore());
            lambdaQueryWrapper.eq(Question::getAnalysis, question.getAnalysis());

            Question question1 = questionMapper.selectOne(lambdaQueryWrapper);

            QuestionAnswer answer = new QuestionAnswer();
            answer.setQuestionId(question1.getId());

            if (questionImportVo.getType().equals("CHOICE")){
                List<QuestionImportVo.ChoiceImportDto> choices = questionImportVo.getChoices();
                for (int i = 0; i < choices.size(); i++) {
                    QuestionChoice choice = new QuestionChoice(); // 在循环内创建新对象
                    choice.setQuestionId(question1.getId());
                    choice.setContent(choices.get(i).getContent());
                    choice.setIsCorrect(choices.get(i).getIsCorrect());
                    choice.setSort(i + 1);
                    if (choices.get(i).getIsCorrect().equals(true)){
                        answer.setAnswer(choices.get(i).getContent());
                    }
                    questionChoiceMapper.insert(choice);
                }

            }else {
                answer.setAnswer(questionImportVo.getAnswer());
            }

            questionAnswerMapper.insert(answer);
            count++;
        }
        return count;
    }

    /**
     * 批量导入题目 [execl]
     * 批量数据库添加
     * 批量redis添加
     * @return
     */
    @Override
    public String importExeclBatchQuestions(MultipartFile file) throws IOException {
        //1.校验
        if (file == null || file.isEmpty()){
            throw new RuntimeException("批量导入的文件为空！");
        }
        String filename = file.getOriginalFilename();
        if (!filename.endsWith(".xls") && !filename.endsWith(".xlsx")){
            throw new RuntimeException("批量导入的文件格式错误，必须是xls或者xlsx格式！");
        }
        //2.解析文件
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //3.调用批量导入业务
        int successCount = extracted(questionImportVoList);
        String result = "批量导入题目接口调用成功！ 一共：%s 题目需要导入，成功导入了：%s 道题！".formatted(questionImportVoList.size(),successCount);
        return result;
    }

    /**
     * 热点题目缓存
     *
     * @param id
     */
    private void incrementQuestion(Long id) {
        Double score = redisUtils.zIncrementScore(POPULAR_QUESTIONS_KEY, id, 1);
        log.info("完成{}题目分数累计，累计后分数为：{}",id,score);
    }


    private void fillQuestionChoiceAndAnswer(List<Question> questionList) {
        //1. 非空判断
        if (questionList == null || questionList.isEmpty()) {
            log.debug("没有查询对应的问题集合数据！！");
            return;
        }
        //2. 查询所有答案和选项
        //优化查询本次题目的答案和选项
        //查询本地题目集合对应的id集合！！
        List<Long> ids = questionList.stream().map(Question::getId).collect(Collectors.toList());
        //查询本次题目的选项集合
        List<QuestionChoice> questionChoiceList = questionChoiceMapper.selectList(new LambdaQueryWrapper<QuestionChoice>()
                .in(QuestionChoice::getQuestionId, ids));
        //查询本次题目的答案
        List<QuestionAnswer> questionAnswers = questionAnswerMapper.selectList(new LambdaQueryWrapper<QuestionAnswer>()
                .in(QuestionAnswer::getQuestionId, ids));

        //3. 答案和选项进行map转化
       /* Map<Long, List<QuestionChoice>> questionChoiceMap =questionChoiceList
                .stream()
                .collect(Collectors.groupingBy(QuestionChoice::getQuestionId));
        Map<Long, QuestionAnswer> answerMap =questionAnswers
                .stream()
                .collect(Collectors.toMap(QuestionAnswer::getQuestionId, a -> a));
*/

        //3. 答案和选项进行map转化
        Map<Long, List<QuestionChoice>> questionChoiceMap = new HashMap<>();
        Map<Long, QuestionAnswer> answerMap = new HashMap<>();
        // 使用传统循环方式替代Stream流构建questionChoiceMap
        for (QuestionChoice choice : questionChoiceList) {
            Long questionId = choice.getQuestionId();
            if (!questionChoiceMap.containsKey(questionId)) {
                questionChoiceMap.put(questionId, new ArrayList<>());
            }
            List<QuestionChoice> questionChoices = questionChoiceMap.get(questionId);
            questionChoices.add(choice);
        }
        // 使用传统循环方式替代Stream流构建answerMap
        for (QuestionAnswer answer : questionAnswers) {
            answerMap.put(answer.getQuestionId(), answer);
        }

        //4. 循环问题集合，进行选项和答案配置
        questionList.forEach(question -> {
            //每个题目一定答案
            question.setAnswer(answerMap.get(question.getId()));
            //选择题才有选项
            if ("CHOICE".equals(question.getType())) {
                List<QuestionChoice> questionChoices = questionChoiceMap.get(question.getId());
                // 添加空值检查，防止NullPointerException
                if (questionChoices != null) {
                    questionChoices.sort(Comparator.comparingInt(QuestionChoice::getSort));
                    question.setChoices(questionChoices);
                }
            }
            question.setCategory(categoryMapper.selectById(question.getCategoryId()));
        });
    }
}